package org.liurb.springboot.starter.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 请求帮助类
 *
 * @Author Liurb
 * @Date 2022/11/28
 */
@Slf4j
public class RequestHttpUtil {

    /**
     * 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址;
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String getIpAddress(HttpServletRequest request) {
        // 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址

        String ip = request.getHeader("X-Forwarded-For");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } else if (ip.length() > 15) {
            String[] ips = ip.split(",");
            for (int index = 0; index < ips.length; index++) {
                String strIp = (String) ips[index];
                if (!("unknown".equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }

    /**
     * 获取get请求的参数
     *
     * @param request
     * @return
     */
    public static Map<String, String> getRequestQueryData(HttpServletRequest request) {
        try {
            String queryString = request.getQueryString();
            if (StrUtil.isNotBlank(queryString)) {
                String queryText = new String(queryString.getBytes("iso-8859-1"), "utf-8").replaceAll("%22", "\"");

                return HttpUtil.decodeParamMap(queryText, Charset.forName("UTF-8"));
            }

        } catch (Exception e) {
            log.error("获取请求参数异常, getRequestQueryData", e);
        }

        return null;
    }

    /**
     * 获取post请求的参数
     *
     * @param request
     * @return
     */
    public static Map<String, Object> getPostRequestData(HttpServletRequest request) {

        //获取请求类型
        String contentType = Optional.ofNullable(request.getContentType()).orElse("");

        if (contentType.contains(ContentType.FORM_URLENCODED.getValue())) {//application/x-www-form-urlencoded类型

            return getFormUrlEncodedData(request);

        } else if (contentType.contains(ContentType.JSON.getValue())) {//application/json类型

            return getJsonData(request);

        }

        return Collections.emptyMap();
    }

    /**
     * 获取application/x-www-form-urlencoded类型的请求数据
     *
     * @param request
     * @return
     */
    public static Map<String, Object> getFormUrlEncodedData(HttpServletRequest request) {

        try {
            String requestBody = getRequestPostBody(request);

            return getFormUrlEncodedData(requestBody);

        } catch (IOException e) {

            log.error("获取请求参数异常, getFormUrlEncodedData", e);
        }

        return Collections.emptyMap();
    }

    /**
     * 获取application/x-www-form-urlencoded类型的请求数据
     *
     * @param requestBody
     * @return
     */
    public static Map<String, Object> getFormUrlEncodedData(String requestBody) {

        try {
            //HttpUtil.decodeParamMap方法在5.5.x版本以上不再处理空格转义为加号的问题
            // 对于"application/x-www-form-urlencoded"方式先使用decode方式进行处理
            Map<CharSequence, CharSequence> queryMap = UrlQuery.of(requestBody, StandardCharsets.UTF_8, false, true).getQueryMap();
            Map<String, String> map = MapUtil.isEmpty(queryMap) ? MapUtil.empty() : Convert.toMap(String.class, String.class, queryMap);

            return stringMapToObjectMap(map);

        } catch (Exception e) {

            log.error("获取请求参数异常, getFormUrlEncodedData", e);
        }

        return Collections.emptyMap();
    }

    /**
     * 获取application/json类型的请求数据
     *
     * @param request
     * @return
     */
    public static Map<String, Object> getJsonData(HttpServletRequest request) {

        try {
            String requestBody = getRequestPostBody(request);

            return getJsonData(requestBody);

        } catch (Exception e) {

            log.error("获取请求参数异常, getJsonData", e);
        }

        return Collections.emptyMap();
    }

    /**
     * 获取application/json类型的请求数据
     *
     * @param requestBody
     * @return
     */
    public static Map<String, Object> getJsonData(String requestBody) {

        try {

            if (StrUtil.isNotBlank(requestBody) && JSONUtil.isTypeJSON(requestBody)) {// 判断是否为json格式
                return JSONUtil.toBean(requestBody, new TypeReference<Map<String, Object>>(){}, false);
            }

        } catch (Exception e) {

            log.error("获取请求参数异常, getJsonData", e);
        }

        return Collections.emptyMap();
    }

    /**
     * 获取 post 请求内容
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static String getRequestPostBody(HttpServletRequest request) throws IOException {
        byte buffer[] = getRequestPostBytes(request);
        String charEncoding = request.getCharacterEncoding();
        if (charEncoding == null) {
            charEncoding = "UTF-8";
        }
        if (buffer == null) {
            return "";
        }
        String body = new String(buffer, charEncoding);
        log.info("原Post请求Body信息: {}", body);
        return body;
    }

    /**
     * 获取post请求的参数
     *
     * 主要用于restTemplate的日志记录
     *
     * @param contentType
     * @param requestBody
     * @return
     */
    public static Map<String, Object> getPostDataByContentTypeAndBody(String contentType, String requestBody) {

        if (contentType.contains(ContentType.FORM_URLENCODED.getValue())) {//application/x-www-form-urlencoded类型

            return getFormUrlEncodedData(requestBody);

        } else if (contentType.contains(ContentType.JSON.getValue())) {//application/json类型

            return getJsonData(requestBody);

        }

        return Collections.emptyMap();
    }

    /**
     * 获取 post 请求的 byte[] 数组
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static byte[] getRequestPostBytes(HttpServletRequest request) throws IOException {
        int contentLength = request.getContentLength();
        if (contentLength < 0) {
            return null;
        }
        byte buffer[] = new byte[contentLength];
        for (int i = 0; i < contentLength; ) {

            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;
    }

    /**
     * string类型map转object类型map
     *
     * @param stringMap
     * @return
     */
    public static Map<String, Object> stringMapToObjectMap(Map<String, String> stringMap) {

        int mapSize = stringMap.size();
        if (mapSize > 0) {
            Map<String, Object> params = new HashMap<>(mapSize);
            for (String key: stringMap.keySet()) {
                params.put(key, stringMap.get(key));
            }

            return params;
        }

        return Collections.emptyMap();
    }

}
